微服務(Microservices)這幾年非常夯,因為企業行之有年之後,內部系統就變成一隻大象,牽一髮動全身,任何功能的新增或修改,都可能造成既有系統的穩定性,例如前一陣子的微軟系統全球大當機,只是一個防毒檔案的更新,就引發全球PC大當機,因此,微服務架構希望能解構傳統的單體式(Monolithic)應用系統架構,提供輕量級(Lightweight)、去中心化(Decentralized)、鬆散耦合(Loosely coupled)的架構,每一個應用系統只負責單一功能(Single Responsibility),獨立開發/運作,不要與核心系統綁在一起。
圖一. 微服務(Microservices),圖片來源:【When to Use and When NOT to Use Microservices: No Silver Bullet】
2014年軟體工程大老Martin Fowler與James Lewis共同提出微服務的概念,再經其他專家演繹,共整理出9大最佳實踐(),但是各自表述,並不完全一致,筆者整理裡如下:
【ByteByteGo EP106】有一張非常美觀的圖,筆者擔心有版權問題,請讀者自行點閱。
以下透過實作,說明微服務的概念,通常微服務會以REST API的形式呈現,Web Server、Mobile或桌面程式呼叫API存取資料,提供REST API開發的python套件有FastAPI、Django REST framework...等,本文以FastAPI為例實作REST API。
範例1. FastAPI簡單測試。
pip install "fastapi[standard]
from fastapi import FastAPI
# 建立 FastAPI 物件
app = FastAPI()
# 設定路由及其處理函數
@app.get("/")
async def root():
return {"message": "Hello World"}
fastapi dev fastapi1.py
http://localhost:8000/
{"message":"Hello World"}
範例2. 行事曆實作,Server端以FastAPI為例實作REST API,Client端借用【JavaScript Event Calendar for Resource Scheduling】一文的JavaScript元件。
自【JavaScript Event Calendar for Resource Scheduling】下載【DayPilot Lite for JavaScript 2024.3 Event Calendar】,並解壓縮。
複製demo資料夾內的子目錄至24\static資料夾,index.html則複製至24\templates資料夾。
修改css、js路徑以符合Jinja2格式如下,完整內容請參考24\templates\index.html。
<script src="{{ url_for('static', path='/js/daypilot-all.min.js?v=2024.3.547') }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', path='/themes/calendar_green.css') }}"/>
CREATE TABLE "calendar" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(120) NOT NULL, "start" datetime NOT NULL, "end" datetime NOT NULL);
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import sqlite3
from dataclasses import dataclass
from datetime import datetime
app = FastAPI(debug=True, redirect_slashes=False)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@dataclass
class Event():
# id: int
text: str
start:datetime
end:datetime
def execute_SQL(sql):
con = sqlite3.connect("calendar.db")
cur = con.cursor()
result = cur.execute(sql)
con.commit()
con.close()
return result
def execute_select_SQL(sql):
con = sqlite3.connect("calendar.db")
cur = con.cursor()
result = cur.execute(sql)
data = result.fetchall()
con.commit()
con.close()
return data
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse(
"index.html", {"request": request}
)
@app.get("/api/CalendarEvents")
async def get_events(start, end):
sql = f"select * from calendar where start >= '{start}' and end <= '{end}'"
response = []
result = execute_select_SQL(sql)
for row in result:
dict1 = {}
dict1['id'] = row[0]
dict1['text'] = row[1]
dict1['start'] = row[2]
dict1['end'] = row[3]
response.append(dict1)
return response
@app.post("/api/CalendarEvents")
async def add_events(event:Event):
# SSprint(event.start, event.end, event.text)
sql = f"insert into calendar (text, start, end) values ('{event.text}', '{event.start}', '{event.end}')"
result = execute_select_SQL(sql)
dict1 = {}
dict1['id'] = result
dict1['text'] = event.text
dict1['start'] = event.start
dict1['end'] = event.end
return dict1
@app.put("/api/CalendarEvents/{id}")
async def update_events(id, event:Event):
# print(event.start, event.end, event.text)
sql = f"update calendar set text='{event.text}', start='{event.start}', end='{event.end}' "
sql += f" where id={id}"
result = execute_SQL(sql)
return {}
@app.delete("/api/CalendarEvents/{id}")
async def delete_events(id):
sql = f"delete from calendar where id = {id}"
result = execute_SQL(sql)
return {}
fastapi dev fastapi_calendar.py
http://localhost:8000/
執行結果:
行事曆可新增(以滑鼠拖曳一段時間)、刪除、更正、複製、移動(以滑鼠拖曳)及改變起訖時間(以滑鼠拖曳)。
補充說明:
微服務(Microservices)適合較單純且獨立的應用程式開發,最好是非關鍵性的專案,例如【WebEIP Pro】所列項目。
圖二. 適合微服務的專案,圖片來源:【WebEIP PRO企業團隊管理解決方案】
本系列的程式碼會統一放在GitHub,本篇的程式放在src/24資料夾,歡迎讀者下載測試,如有錯誤或疏漏,請不吝指正。